package com.integral.controller;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.InvalidAlgorithmParameterException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.weixin4j.WeixinException;
import org.weixin4j.WeixinSupport;
import org.weixin4j.http.HttpsClient;
import org.weixin4j.http.Response;

import com.alibaba.druid.util.Base64;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.integral.biz.IBalanceBiz;
import com.integral.biz.IOrderBiz;
import com.integral.biz.IUserBiz;
import com.integral.constant.OrderConstant;
import com.integral.entity.Balance;
import com.integral.entity.Order;
import com.integral.entity.Product;
import com.integral.entity.User;
import com.integral.entity.UserInfo;
import com.integral.util.IpUtils;
import com.integral.util.PayUtil;
import com.integral.util.StringUtils;
import com.integral.vo.Json;
import com.integral.vo.OAuthJsToken;
import com.integral.vo.UserVo;
import com.integral.weixin.WxPayConfig;

/**
 * @Description: 本示例仅供参考，请根据自己的使用情景进行修改
 * @Date: 2018/4/8
 * @Author: wcf
 */
@RequestMapping("/weixin")
@RestController
public class WeixinController extends WeixinSupport{
	
	@Resource
	private IUserBiz userBiz;
	
	@Resource
	private IOrderBiz orderBiz;
	
	@Resource
	private IBalanceBiz balanceBiz;

    private Logger logger = LoggerFactory.getLogger(getClass());

    //客户账号
    private static final String appid = "wxf4fea99da7ff3fd5";	    //微信小程序appid
    private static final String secret = "a3aa3e16a57e2d2e56906e541f181026";	//微信小程序密钥
    private static final String grant_type = "authorization_code";

    
    //个人测试帐号
    //private static final String appid = "wxd778aeec0edeb46c";	    //微信小程序appid
    //private static final String secret = "65a7bca5a01e8bacb625a91ca0c8e0dc";	//微信小程序密钥
    /**
     * 小程序后台登录，向微信平台发送请求获取用户唯一标识，并设置用户，返回用户对象
     *
     * @param code
     * @return openid
     * @throws WeixinException
     * @throws IOException
     * @since Weixin4J 1.0.0
     */
    @RequestMapping("/login")
    @ResponseBody
    public Map<String, Object> login(String code, String userSuperiorCode,HttpServletRequest request) throws WeixinException, IOException {
        if (code == null || code.equals("")) {
            throw new WeixinException("invalid null, code is null.");
        }

        Map<String, Object> ret = new HashMap<String, Object>();
        //拼接参数
        String param = "?grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret + "&js_code=" + code;

        //创建请求对象
        HttpsClient http = new HttpsClient();
        //调用微信接口，获取openId以及sessionKey
        Response res = http.get("https://api.weixin.qq.com/sns/jscode2session" + param);
        //根据请求结果判定，是否验证成功
        JSONObject jsonObj = res.asJSONObject();
        if (jsonObj != null) {
            Object errcode = jsonObj.get("errcode");
            if (errcode != null) {
                //返回异常信息
                throw new WeixinException(getCause(Integer.parseInt(errcode.toString())));
            }

            ObjectMapper mapper = new ObjectMapper();
            OAuthJsToken oauthJsToken = mapper.readValue(jsonObj.toJSONString(),OAuthJsToken.class);
            String sessionKey = oauthJsToken.getSession_key();
            logger.info("openid=" + oauthJsToken.getOpenid());
            //解密sessionKey获取用户数据
//            try {    
//                byte[] resultByte  = StringUtils.decrypt(Base64.decodeBase64(userSuperiorCode),    
//                        Base64.decodeBase64(oauthJsToken.getSession_key()),  
//                        Base64.decodeBase64(userSuperiorCode));    
//                    if(null != resultByte && resultByte.length > 0){    
//                        String userInfo = new String(resultByte, "UTF-8");                   
//                    }   
//            }catch (InvalidAlgorithmParameterException e) {    
//                    e.printStackTrace();    
//            } catch (UnsupportedEncodingException e) {    
//                    e.printStackTrace();    
//            }
            
            //设置用户（若用户存在，则返回用户信息。若用户不存在，则新增用户(若携带上级用户编码，则设置上级用户)）
            User user = userBiz.findUserAddUser(oauthJsToken.getOpenid(), null, userSuperiorCode);
            ret.put("user", user);
        }
        return ret;
    }

    /**
     * @Description: 发起微信支付
     * @param openid
     * @param request
     * @author: wcf
     * @date: 2017年8月28日
     */
    @RequestMapping("/wxPay")
    @ResponseBody
    public Json wxPay(String openid,int productId, HttpServletRequest request){
        Json json = new Json();
        //商品名称
        String productName = "测试商品";
        //商品金额(单位：分，这边需要转成字符串类型，否则后面的签名会失败)
        String productMoney = "1";
        //订单编码
        String productNo = "D"+StringUtils.getRandomStringByLength(5)+new Date().getTime();
        //金额倍数（分）传入的金额是元，需要乘以100分，得到最终支付金额
    	BigDecimal fen = new BigDecimal(100);
        Product product = orderBiz.findProductById(productId);
        if(null != product){
        	//数据库中存的是元（需要乘以100，得到以分为单位的最终支付金额）
        	int mm = product.getProductMoney().multiply(fen).intValue();
        	productName = product.getProductName();
        	productMoney = String.valueOf(mm);
        }
        try{
            //生成的随机字符串
            String nonce_str = StringUtils.getRandomStringByLength(32);
            //获取本机的ip地址
            String spbill_create_ip = IpUtils.getIpAddr(request);
            //订单号
            String orderNo = productNo;

            String notifyUrl = request.getServletContext().getRealPath("/weixin/wxNotify");
            Map<String, String> packageParams = new HashMap<String, String>();
            packageParams.put("appid", WxPayConfig.appid);
            packageParams.put("mch_id", WxPayConfig.mch_id);
            packageParams.put("nonce_str", nonce_str);
            packageParams.put("body", productName);	//商品名称
            packageParams.put("out_trade_no", orderNo);//商户订单号
            packageParams.put("total_fee", productMoney);//支付金额，单位：分，这边需要转成字符串类型，否则后面的签名会失败
            packageParams.put("spbill_create_ip", spbill_create_ip);
            packageParams.put("notify_url", notifyUrl);
            packageParams.put("trade_type", WxPayConfig.TRADETYPE);
            packageParams.put("openid", openid);

            // 除去数组中的空值和签名参数
            packageParams = PayUtil.paraFilter(packageParams);
            String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串

            //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
            String mysign = PayUtil.sign(prestr, WxPayConfig.key, "utf-8").toUpperCase();
            logger.info("=======================第一次签名：" + mysign + "=====================");

            //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
            String xml = "<xml>" + "<appid>" + WxPayConfig.appid + "</appid>"
                    + "<body><![CDATA[" + productName + "]]></body>"
                    + "<mch_id>" + WxPayConfig.mch_id + "</mch_id>"
                    + "<nonce_str>" + nonce_str + "</nonce_str>"
                    + "<notify_url>" + notifyUrl + "</notify_url>"
                    + "<openid>" + openid + "</openid>"
                    + "<out_trade_no>" + orderNo + "</out_trade_no>"
                    + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
                    + "<total_fee>" + productMoney + "</total_fee>"
                    + "<trade_type>" + WxPayConfig.TRADETYPE + "</trade_type>"
                    + "<sign>" + mysign + "</sign>"
                    + "</xml>";

            logger.warn("调试模式_统一下单接口 请求XML数据：" + xml);

            //调用统一下单接口，并接受返回的结果
            String result = PayUtil.httpRequest(WxPayConfig.pay_url, "POST", xml);

            logger.warn("调试模式_统一下单接口 返回XML数据：" + result);

            // 将解析结果存储在HashMap中
            Map map = PayUtil.doXMLParse(result);

            String return_code = (String) map.get("return_code");//返回状态码

            //返回给移动端需要的参数
            Map<String, Object> response = new HashMap<String, Object>();
            if(return_code == "SUCCESS" || return_code.equals(return_code)){
                // 业务结果
                String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
                response.put("nonceStr", nonce_str);
                response.put("package", "prepay_id=" + prepay_id);
                Long timeStamp = System.currentTimeMillis() / 1000;
                response.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串，不然小程序端调用wx.requestPayment方法会报签名错误
                String stringSignTemp = "appId=" + WxPayConfig.appid + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id+ "&signType=" + WxPayConfig.SIGNTYPE + "&timeStamp=" + timeStamp;
                //再次签名，这个签名用于小程序端调用wx.requesetPayment方法
                String paySign = PayUtil.sign(stringSignTemp, WxPayConfig.key, "utf-8").toUpperCase();
                logger.info("=======================第二次签名：" + paySign + "=====================");
                response.put("paySign", paySign);
                //小程序回调使用到的参数
                response.put("outTradeNo", orderNo);//订单号
                response.put("openid", openid);	//用户唯一标识
                response.put("productName", productName);//商品名称
                response.put("productMoney", productMoney);//商品金额
                response.put("productId", productId);//产品id
                response.put("requestXML", xml);//支付请求的xml
                response.put("responseXML", result);//微信返回的xml
                //业务逻辑代码
                UserVo user = userBiz.findByUserOpenid(openid);
                Order order = new Order();
                //订单号
                order.setOrderNo(orderNo);
                //金额
                order.setExchangeMoney(product.getProductMoney());
                //订单类型
                order.setOrderType(OrderConstant.ORDER_TYPE_RECHARGE);
                //订单状态
                order.setOrderStatus(OrderConstant.ORDER_STATUS_UNTREATED);
                //支付产品Id
                order.setProductId(productId);
                //支付请求的xml
                order.setRequestXML(xml);
                //微信返回的xml
                order.setResponseXML(result);
                if(null != user){
                	//用户编码
                	order.setUserCode(user.getUserCode());
                }
                orderBiz.addOrder(order);
            }

            response.put("appid", WxPayConfig.appid);
            json.setSuccess(true);
            json.setData(response);
        }catch(Exception e){
            e.printStackTrace();
            json.setSuccess(false);
            json.setMsg("发起失败");
        }
        return json;
    }

    /**
     * @Description:微信支付
     * @return
     * @author dzg
     * @throws Exception
     * @throws WeixinException
     * @date 2016年12月2日
     */
    @RequestMapping(value="/wxNotify")
    @ResponseBody
    public void wxNotify(HttpServletRequest request,HttpServletResponse response) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine())!=null){
            sb.append(line);
        }
        br.close();
        //sb为微信返回的xml
        String notityXml = sb.toString();
        String resXml = "";
        System.out.println("接收到的报文：" + notityXml);

        Map map = PayUtil.doXMLParse(notityXml);

        String returnCode = (String) map.get("return_code");
        if("SUCCESS".equals(returnCode)){
            //验证签名是否正确
            if(PayUtil.verify(PayUtil.createLinkString(map), (String)map.get("sign"), WxPayConfig.key, "utf-8")){
                /**此处添加自己的业务逻辑代码start**/
            	
                /**此处添加自己的业务逻辑代码end**/
                //通知微信服务器已经支付成功
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            }
        }else{
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        }
        System.out.println(resXml);
        System.out.println("微信支付回调数据结束");

        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }
    /**
     * 提现
     * @param money
     * @param userOpenId
     * @param request
     * @return
     */
    @RequestMapping(value="/withdraw")
    @ResponseBody
    public String withdraw(BigDecimal money,String userOpenId, HttpServletRequest request){
    	String productNo = "D"+StringUtils.getRandomStringByLength(5)+new Date().getTime();
    	//新增订单
		UserVo userVo = userBiz.findByUserOpenid(userOpenId);
		Order order = new Order();
        //订单号
        order.setOrderNo(productNo);
        //金额
        order.setExchangeMoney(money);
        //订单类型
        order.setOrderType(OrderConstant.ORDER_TYPE_DEPOSIT);
        //订单状态
        order.setOrderStatus(OrderConstant.ORDER_STATUS_UNTREATED);
        //支付请求的xml
        //order.setRequestXML(xml);
        //微信返回的xml
        //order.setResponseXML(result);
        if(null != userVo){
        	//用户编码
        	order.setUserCode(userVo.getUserCode());
        }
        orderBiz.addOrder(order);
        
        //修改订单余额
        Balance balance = balanceBiz.findByUserCode(userVo.getUserCode());
        if(null != balance){
        	balance.setUserMoney(balance.getUserMoney().subtract(money));
        	balanceBiz.updateBalance(balance);
        }
        return "提现请求审核中！";
    }
    /**
     * 企业支付
     * @param money 金额（元）
     * @param userOpenId 用户唯一标识
     * @param request
     * @return
     */
    @RequestMapping(value="/wxPayment")
    @ResponseBody
    public Map<String,Object> wxPayment(BigDecimal money,String userCode, String productNo,HttpServletRequest request){
    	Map<String,Object> response = new HashMap<>();
    	try {
	    	//金额倍数（分）传入的金额是元，需要乘以100分，得到最终支付金额
	    	BigDecimal yuan = new BigDecimal(100);
	    	//最终支付金额
	    	String userMoney = money.multiply(yuan).toString();
	    	if(userMoney.indexOf(".") > 0){
	    		userMoney = userMoney.substring(0,userMoney.indexOf("."));
	    	}
	    	User user = userBiz.findByCode(userCode);
	    	//String userMoney = "1";
	    	//订单编码（商户订单）
	        //String productNo = "D"+StringUtils.getRandomStringByLength(5)+new Date().getTime();
	    	//生成的随机字符串
	        String nonce_str = StringUtils.getRandomStringByLength(32);
	        //获取本机的ip地址
	        String spbill_create_ip = IpUtils.getIpAddr(request);
	        Map<String, String> packageParams = new HashMap<String, String>();
	        packageParams.put("mch_appid", WxPayConfig.appid);
	        packageParams.put("mchid", WxPayConfig.mch_id);
	        packageParams.put("nonce_str", nonce_str);	//随机字符串
	        packageParams.put("partner_trade_no", productNo);//商户订单号
	        packageParams.put("openid", user.getUserOpenid());	//用户唯一标识
	        packageParams.put("check_name", "NO_CHECK");//校验用户姓名选项NO_CHECK：不校验真实姓名  FORCE_CHECK：强校验真实姓名
	        packageParams.put("amount", userMoney);//支付金额，这边需要转成字符串类型，否则后面的签名会失败
	        packageParams.put("desc", "积分乐分享提现");//企业付款描述信息
	        packageParams.put("spbill_create_ip", spbill_create_ip);
	        
	        // 除去数组中的空值和签名参数
	        packageParams = PayUtil.paraFilter(packageParams);
	        String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
	
	        //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
	        String mysign = PayUtil.sign(prestr, WxPayConfig.key, "utf-8").toUpperCase();
	        logger.info("=======================第一次签名：" + mysign + "=====================");
	
	        //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
	        String xml = "<xml>" + "<mch_appid>" + WxPayConfig.appid + "</mch_appid>"
	                + "<mchid>" + WxPayConfig.mch_id + "</mchid>"
	                + "<nonce_str>" + nonce_str + "</nonce_str>"
	                + "<partner_trade_no>" + productNo + "</partner_trade_no>"
	                + "<openid>" + user.getUserOpenid() + "</openid>"
	                + "<check_name>" + "NO_CHECK" + "</check_name>"
	                + "<amount>" + userMoney + "</amount>"
	                + "<desc>" + "积分乐分享提现" + "</desc>"
	                + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
	                + "<sign>" + mysign + "</sign>"
	                + "</xml>";
	
	        logger.warn("调试模式_企业支付接口 请求XML数据：" + xml);
	        //调用统一下单接口，并接受返回的结果
	        //String result = PayUtil.httpRequest(WxPayConfig.payment_url, "POST", xml);
	        String result = PayUtil.doRefund(WxPayConfig.payment_url, xml, request);
	        logger.warn("调试模式_企业支付接口 返回XML数据：" + result);
	        // 将解析结果存储在HashMap中
	        Map map;
			map = PayUtil.doXMLParse(result);
			//返回状态码
	        String return_code = (String) map.get("return_code");
	        
            if(return_code == "SUCCESS" || return_code.equals(return_code)){
            	//提现成功
            	if("SUCCESS".equals(map.get("result_code"))){
            		HttpSession session = request.getSession();
            		UserInfo userInfo = (UserInfo)session.getAttribute("userInfo");
            		Order order = new Order();
            		order.setOrderNo(productNo);
            		order.setOrderStatus(OrderConstant.ORDER_STATUS_SUCCESS);
            		order.setCreateTime(new Date());
            		order.setCreateEmp(userInfo.getUserName());
            		orderBiz.updateByStatus(order);
//            		//新增订单
//            		UserVo userVo = userBiz.findByUserOpenid(userOpenId);
//            		Order order = new Order();
//                    //订单号
//                    order.setOrderNo(productNo);
//                    //金额
//                    order.setExchangeMoney(money);
//                    //订单类型
//                    order.setOrderType(OrderConstant.ORDER_TYPE_DEPOSIT);
//                    //订单状态
//                    order.setOrderStatus(OrderConstant.ORDER_STATUS_SUCCESS);
//                    //支付请求的xml
//                    order.setRequestXML(xml);
//                    //微信返回的xml
//                    order.setResponseXML(result);
//                    if(null != userVo){
//                    	//用户编码
//                    	order.setUserCode(userVo.getUserCode());
//                    }
//                    orderBiz.addOrder(order);
//                    
//                    //修改订单余额
//                    Balance balance = balanceBiz.findByUserCode(userVo.getUserCode());
//                    if(null != balance){
//                    	balance.setUserMoney(balance.getUserMoney().subtract(money));
//                    	balanceBiz.updateBalance(balance);
//                    }
            	}
                //商户appid
                response.put("mch_appid", map.get("mch_appid"));
                //商户号
                response.put("mchid", map.get("mchid"));
                //业务结果
                response.put("result_code", map.get("result_code"));
                //错误代码
            	response.put("err_code", map.get("err_code"));
            	//错误代码描述
            	response.put("err_code_des", map.get("err_code_des"));
                //返回成功标记
            	response.put("return", "success");
            }else{
            	//错误代码
            	response.put("err_code", map.get("err_code"));
            	//错误代码描述
            	response.put("err_code_des", map.get("err_code_des"));
            	//返回成功标记
            	response.put("return", "");
            }
            //返回成功标记
        	response.put("return", "success");
	        
		} catch (Exception e) {
			//返回成功标记
			response.put("return", "error");
	    	return response;
		}
    	
    	return response;
    }
    /**
     * 生成二维码
     * @return
     */
    @RequestMapping(value="/createQRCode")
    @ResponseBody
    public String createQRCode(String userCode,HttpServletRequest request) throws WeixinException, IOException{
    	/** 获取access_token */
    	//拼接参数
        String param = "?grant_type=client_credential" + "&appid=" + appid + "&secret=" + secret ;
        //创建请求对象
        HttpsClient http = new HttpsClient();
        //调用微信接口，获取access_token
		Response res = http.get("https://api.weixin.qq.com/cgi-bin/token" + param);
		//根据请求结果判定，是否验证成功
		JSONObject jsonObj = res.asJSONObject();
		ObjectMapper mapper = new ObjectMapper();
        OAuthJsToken oauthJsToken = mapper.readValue(jsonObj.toJSONString(),OAuthJsToken.class);
        String accessToken = oauthJsToken.getAccess_token();
    	if(StringUtils.isEmpty(accessToken)){
    		return "获取accessToken失败！";
    	}
    	/** 获取二维码 */
    	String fileName = getminiqrQr(userCode, accessToken,request);
    	return fileName;
    }
    /**
     * 调用微信接口，获取二维码
     * @param sceneStr
     * @param accessToken
     * @return
     */
    public String getminiqrQr(String sceneStr, String accessToken,HttpServletRequest request) {
        RestTemplate rest = new RestTemplate();
        InputStream inputStream = null;
        OutputStream outputStream = null;
        String fileName = null;
        try {
            String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="+accessToken;
            Map<String,Object> param = new HashMap<>();
            param.put("scene", sceneStr);
            param.put("page", "pages/index/index");
            param.put("width", 430);
            param.put("auto_color", false);
            Map<String,Object> line_color = new HashMap<>();
            line_color.put("r", 0);
            line_color.put("g", 0);
            line_color.put("b", 0);
            param.put("line_color", line_color);
            logger.info("调用生成微信URL接口传参:" + param);
            MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
            HttpEntity requestEntity = new HttpEntity(param, headers);
            ResponseEntity<byte[]> entity = rest.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]);
            logger.info("调用小程序生成微信永久小程序码URL接口返回结果:" + entity.getBody());
            byte[] result = entity.getBody();
            inputStream = new ByteArrayInputStream(result);
            fileName = String.valueOf(System.currentTimeMillis())+".png";
            String filePath = request.getServletContext().getRealPath("/management/QRImg/")+fileName;
            File file = new File(filePath);
            if (!file.exists()){
                file.createNewFile();
            }
            outputStream = new FileOutputStream(file);
            int len = 0;
            byte[] buf = new byte[1024];
            while ((len = inputStream.read(buf, 0, 1024)) != -1) {
                outputStream.write(buf, 0, len);
            }
            outputStream.flush();
        } catch (Exception e) {
        	logger.error("调用小程序生成微信永久小程序码URL接口异常",e);
        } finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return fileName;
    }
}
